package cn.iocoder.yudao.module.written.exam.service.llm;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.module.written.exam.controller.app.writtenexam.vo.llm.LlmChatReqVO;
import cn.iocoder.yudao.module.written.exam.dal.dataobject.llm.*;
import cn.iocoder.yudao.module.written.exam.enums.llm.LlmMessageStatusEnum;
import cn.iocoder.yudao.module.written.exam.enums.llm.LlmMessageTypeEnum;
import cn.iocoder.yudao.module.written.exam.utils.LlmEventUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;

/**
 * 大模型聊天 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class LlmChatServiceImpl implements LlmChatService {


    /**
     * 大模型API地址
     */
    @Value("${llm.api.url:http://113.44.174.37:1880/v1/chat-messages}")
    private String llmApiUrl;

    /**
     * 大模型API Token
     */
    @Value("${llm.api.token:app-8vXaynkr2pS5kP9rIdqodlAb}")
    private String llmApiToken;


    @Override
    public List<String> uploadFiles(List<String> filePaths) {
        List<String> fileIds = new ArrayList<>();

        try {
            // 1. 参数校验
            if (filePaths == null || filePaths.isEmpty()) {
                throw ServiceExceptionUtil.exception(BAD_REQUEST, "文件路径列表不能为空");
            }

            if (filePaths.size() > 3) {
                throw ServiceExceptionUtil.exception(BAD_REQUEST, "最多只能上传3个文件");
            }

            // 2. 循环调用原先的单文件上传方法
            for (String filePath : filePaths) {
                String fileId = uploadFile(filePath);
                fileIds.add(fileId);
            }

            log.info("批量文件上传到大模型平台成功：fileIds={}, filePaths={}", fileIds, filePaths);
            return fileIds;

        } catch (Exception e) {
            log.error("批量上传文件到大模型平台失败：filePaths={}, error={}", filePaths, e.getMessage(), e);
            throw ServiceExceptionUtil.exception(BAD_REQUEST, "批量文件上传失败：" + e.getMessage());
        }
    }

    /**
     * 上传单个文件到大模型平台（原先的方法）
     */
    private String uploadFile(String filePath) {
        try {
            // 1. 参数校验
            if (filePath == null || filePath.isEmpty()) {
                throw ServiceExceptionUtil.exception(BAD_REQUEST, "文件路径不能为空");
            }

            // 2. 检查文件是否存在
            File file = new File(filePath);
            if (!file.exists()) {
                throw ServiceExceptionUtil.exception(BAD_REQUEST, "文件不存在：" + filePath);
            }

            // 3. 构建上传URL
            String uploadUrl = llmApiUrl.replace("/chat-messages", "/files/upload");

            // 4. 调用大模型API上传文件 - 使用芋道HttpUtils风格的try-with-resources
            try (HttpResponse response = HttpRequest.post(uploadUrl)
                    .header("Authorization", "Bearer " + llmApiToken)
                    .form("file", file)
                    .form("user", "system")
                    .execute()) {

                // 5. 检查响应状态
                if (!response.isOk()) {
                    String errorMsg = "文件上传失败：HTTP " + response.getStatus() + " - " + response.body();
                    log.error(errorMsg);
                    throw ServiceExceptionUtil.exception(BAD_REQUEST, errorMsg);
                }

                // 6. 解析响应获取文件ID
                String responseBody = response.body();
                JSONObject jsonResponse = JSONUtil.parseObj(responseBody);
                String fileId = jsonResponse.getStr("id");

                if (StrUtil.isBlank(fileId)) {
                    throw ServiceExceptionUtil.exception(BAD_REQUEST, "上传文件响应异常，未获取到文件ID");
                }

                log.info("文件上传到大模型平台成功：fileId={}, filePath={}", fileId, filePath);
                return fileId;
            }

        } catch (Exception e) {
            log.error("上传文件到大模型平台失败：filePath={}, error={}", filePath, e.getMessage(), e);
            throw ServiceExceptionUtil.exception(BAD_REQUEST, "文件上传失败：" + e.getMessage());
        }
    }

    @Override
    public Flux<LlmEventBase> sendMessage(@Valid LlmChatReqVO reqVO, String userIp, String query) {
        return Flux.create(sink -> {
            try {
                processChatRequestFlux(reqVO, sink, query);
            } catch (Exception e) {
                log.error("处理聊天请求失败：{}", e.getMessage(), e);
                // 发送错误事件
                LlmEventBase errorEvent = LlmEventUtil.createErrorEvent(
                        reqVO.getConversationId(),
                        null,
                        null,
                        "处理聊天请求失败：" + e.getMessage()
                );
                sink.next(errorEvent);
                sink.complete();
            }
        });
    }


    /**
     * 处理聊天请求的核心逻辑（Flux版本）
     */
    private void processChatRequestFlux(LlmChatReqVO reqVO, FluxSink<LlmEventBase> sink, String query) {
        String conversationId = reqVO.getConversationId();
        String taskId = IdUtil.fastSimpleUUID();

        // 固定的用户ID和查询内容
        String userId = "abc-123";

        try {
            // 3. 构建大模型请求
            Map<String, Object> llmRequest = buildLlmRequest(reqVO, conversationId, userId, query);

            // 4. 调用大模型API
            callLlmApiAndStreamResponseFlux(llmRequest, sink, conversationId, taskId);

        } catch (Exception e) {
            log.error("处理聊天请求失败：conversationId={}, error={}", conversationId, e.getMessage(), e);
            // 发送错误事件
            LlmEventBase errorEvent = LlmEventUtil.createErrorEvent(
                    conversationId,
                    null,
                    taskId,
                    "处理聊天请求失败：" + e.getMessage()
            );
            sink.next(errorEvent);
            sink.complete();
        }
    }


    /**
     * 构建大模型请求
     */
    private Map<String, Object> buildLlmRequest(LlmChatReqVO reqVO, String conversationId, String userId, String query) {
        Map<String, Object> request = new HashMap<>();
        request.put("query", query);
        request.put("user", userId);
        request.put("response_mode", "streaming");

        if (StrUtil.isNotBlank(conversationId)) {
            request.put("conversation_id", conversationId);
        }

        // 构建inputs参数
        Map<String, Object> inputs = new HashMap<>();

        // 处理图片列表
        if (reqVO.getExamImgs() != null && !reqVO.getExamImgs().isEmpty()) {
            List<Map<String, Object>> examImgsList = new ArrayList<>();
            for (String fileId : reqVO.getExamImgs()) {
                Map<String, Object> imageInput = new HashMap<>();
                imageInput.put("type", "image");
                imageInput.put("transfer_method", "local_file");
                imageInput.put("upload_file_id", fileId);
                examImgsList.add(imageInput);
            }
            inputs.put("examImgs", examImgsList);
        }

        // 添加其他业务参数
        if (StrUtil.isNotBlank(reqVO.getExamType())) {
            inputs.put("examType", reqVO.getExamType());
        }
        if (StrUtil.isNotBlank(reqVO.getPosition())) {
            inputs.put("position", reqVO.getPosition());
        }
        if (StrUtil.isNotBlank(reqVO.getLanguage())) {
            inputs.put("language", reqVO.getLanguage());
        }
        if (StrUtil.isNotBlank(reqVO.getQuestionType())) {
            inputs.put("questionType", reqVO.getQuestionType());
        }
        if (StrUtil.isNotBlank(reqVO.getAnswerExpert())) {
            inputs.put("answerExpert", reqVO.getAnswerExpert());
        }

        request.put("inputs", inputs);
        return request;
    }

    /**
     * 调用大模型API并流式响应（Flux版本）
     */
    private void callLlmApiAndStreamResponseFlux(Map<String, Object> request, FluxSink<LlmEventBase> sink,
                                                 String conversationId, String taskId) {
        long startTime = System.currentTimeMillis();

        try {
            // 调用大模型API
            HttpResponse response = HttpRequest.post(llmApiUrl)
                    .header("Authorization", "Bearer " + llmApiToken)
                    .header("Content-Type", "application/json")
                    .body(JSONUtil.toJsonStr(request))
                    .execute();

            if (!response.isOk()) {
                throw new RuntimeException("大模型API调用失败：" + response.getStatus() + " - " + response.body());
            }

            // 处理流式响应
            String responseBody = response.body();
            String[] lines = responseBody.split("\n");

            for (String line : lines) {
                if (line.startsWith("data: ") && !line.equals("data: [DONE]")) {
                    String jsonData = line.substring(6);
                    try {
                        JSONObject data = JSONUtil.parseObj(jsonData);
                        // 解析不同类型的事件并转换为对应的 LlmEventBase 子类
                        LlmEventBase eventObj = parseEventFromJson(data);
                        if (eventObj != null) {
                            sink.next(eventObj);
                        }
                    } catch (Exception e) {
                        log.warn("解析SSE数据失败：{}", e.getMessage());
                    }
                }
            }

            sink.complete();

        } catch (Exception e) {
            log.error("调用大模型API失败：{}", e.getMessage(), e);

            // 保存失败的助手消息
            saveFailedAssistantMessage(null, conversationId, e.getMessage(),
                    startTime, "abc-123");

            // 发送错误事件
            LlmEventBase errorEvent = LlmEventUtil.createErrorEvent(
                    conversationId,
                    null,
                    taskId,
                    "调用大模型API失败：" + e.getMessage()
            );
            sink.next(errorEvent);
            sink.complete();
        }
    }

    /**
     * 保存失败的助手消息
     */
    private void saveFailedAssistantMessage(String messageId, String conversationId, String errorMessage,
                                            long startTime, String userId) {
        long responseTime = System.currentTimeMillis() - startTime;

        LlmMessageDO assistantMessage = LlmMessageDO.builder()
                .id(messageId)
                .conversationId(conversationId)
                .messageType(LlmMessageTypeEnum.ASSISTANT.getType())
                .content("处理失败")
                .userId(userId)
                .status(LlmMessageStatusEnum.FAILED.getStatus())
                .errorMessage(errorMessage)
                .responseTime(responseTime)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();

    }


    /**
     * 从 JSON 数据解析事件对象
     */
    private LlmEventBase parseEventFromJson(JSONObject data) {
        try {
            String event = data.getStr("event");
            if (StrUtil.isBlank(event)) {
                return null;
            }

            switch (event) {
                case "workflow_started":
                    return parseWorkflowStartedEvent(data);
                case "message":
                    return parseMessageEvent(data);
                case "workflow_finished":
                    return parseWorkflowFinishedEvent(data);
                case "node_finished":
                    return parseNodeFinishedEvent(data);
                case "message_end":
                    return parseMessageEndEvent(data);
                default:
                    log.warn("未知的事件类型：{}", event);
                    return null;
            }
        } catch (Exception e) {
            log.error("解析事件对象失败：{}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 解析工作流开始事件
     */
    private WorkflowStartedEvent parseWorkflowStartedEvent(JSONObject data) {
        WorkflowStartedEvent event = new WorkflowStartedEvent();
        event.setEvent(data.getStr("event"));
        event.setConversationId(data.getStr("conversation_id"));
        event.setMessageId(data.getStr("message_id"));
        event.setCreatedAt(data.getLong("created_at"));
        event.setTaskId(data.getStr("task_id"));
        event.setWorkflowRunId(data.getStr("workflow_run_id"));

        JSONObject dataObj = data.getJSONObject("data");
        if (dataObj != null) {
            WorkflowStartedData startedData = JSONUtil.toBean(dataObj, WorkflowStartedData.class);
            event.setData(startedData);
        }

        return event;
    }

    /**
     * 解析消息事件
     */
    private MessageEvent parseMessageEvent(JSONObject data) {
        MessageEvent event = new MessageEvent();
        event.setEvent(data.getStr("event"));
        event.setConversationId(data.getStr("conversation_id"));
        event.setMessageId(data.getStr("message_id"));
        event.setCreatedAt(data.getLong("created_at"));
        event.setTaskId(data.getStr("task_id"));
        event.setId(data.getStr("id"));
        event.setAnswer(data.getStr("answer"));

        // 解析变量选择器来源
        if (data.containsKey("from_variable_selector")) {
            event.setFromVariableSelector(data.getBeanList("from_variable_selector", String.class));
        }

        return event;
    }

    /**
     * 解析工作流完成事件
     */
    private WorkflowFinishedEvent parseWorkflowFinishedEvent(JSONObject data) {
        WorkflowFinishedEvent event = new WorkflowFinishedEvent();
        event.setEvent(data.getStr("event"));
        event.setConversationId(data.getStr("conversation_id"));
        event.setMessageId(data.getStr("message_id"));
        event.setCreatedAt(data.getLong("created_at"));
        event.setTaskId(data.getStr("task_id"));
        event.setWorkflowRunId(data.getStr("workflow_run_id"));

        JSONObject dataObj = data.getJSONObject("data");
        if (dataObj != null) {
            WorkflowFinishedData finishedData = JSONUtil.toBean(dataObj, WorkflowFinishedData.class);
            event.setData(finishedData);
        }

        return event;
    }

    /**
     * 解析节点完成事件
     */
    private NodeFinishedEvent parseNodeFinishedEvent(JSONObject data) {
        NodeFinishedEvent event = new NodeFinishedEvent();
        event.setEvent(data.getStr("event"));
        event.setConversationId(data.getStr("conversation_id"));
        event.setMessageId(data.getStr("message_id"));
        event.setCreatedAt(data.getLong("created_at"));
        event.setTaskId(data.getStr("task_id"));
        event.setWorkflowRunId(data.getStr("workflow_run_id"));

        JSONObject dataObj = data.getJSONObject("data");
        if (dataObj != null) {
            NodeFinishedData nodeData = JSONUtil.toBean(dataObj, NodeFinishedData.class);
            event.setData(nodeData);
        }

        return event;
    }

    /**
     * 解析消息结束事件
     */
    private MessageEndEvent parseMessageEndEvent(JSONObject data) {
        MessageEndEvent event = new MessageEndEvent();
        event.setEvent(data.getStr("event"));
        event.setConversationId(data.getStr("conversation_id"));
        event.setMessageId(data.getStr("message_id"));
        event.setCreatedAt(data.getLong("created_at"));
        event.setTaskId(data.getStr("task_id"));
        event.setId(data.getStr("id"));

        JSONObject metadataObj = data.getJSONObject("metadata");
        if (metadataObj != null) {
            Metadata metadata = JSONUtil.toBean(metadataObj, Metadata.class);
            event.setMetadata(metadata);
        }

        if (data.containsKey("files")) {
            event.setFiles(data.getBeanList("files", Object.class));
        }

        return event;
    }

} 